package org.meb.lotrlcg.scan.de;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.Node;
import org.jsoup.select.Elements;
import org.meb.lotrlcg.db.model.CardSetBase;
import org.meb.lotrlcg.db.model.CardSetLang;
import org.meb.lotrlcg.db.model.EncounterSetBase;
import org.meb.lotrlcg.db.model.EncounterSetLang;
import org.meb.lotrlcg.db.model.ScenarioBase;
import org.meb.lotrlcg.db.model.ScenarioLang;
import org.meb.lotrlcg.db.util.Utils;
import org.meb.lotrlcg.json.JsonUtils;
import org.meb.lotrlcg.scan.CardHandlerChain;
import org.meb.lotrlcg.scan.CountCardHandler;
import org.meb.lotrlcg.scan.WriteJsonCardHandler;
import org.meb.lotrlcg.scan.model.Card;
import org.meb.lotrlcg.scan.model.CardSetInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class DeckbauerParser {

	private static final Logger log = LoggerFactory.getLogger(DeckbauerParser.class);

	private final static String MAIN_URL = "http://deckbauer.telfador.net/cards.html&gameid=1";
	private final static String URL_BASE = "http://deckbauer.telfador.net/cards.html?unique=1&productid=${productId}&gameid=1&action=search";
	private final static String URL_SCEN = "http://deckbauer.telfador.net/decks.html&gameid=1&decktypeid=3";
	private final static String URL_CRST = "http://deckbauer.telfador.net/products.html&gameid=1";

	private static final Map<String, String> imgMap;

	static {
		imgMap = new HashMap<String, String>();
		imgMap.put("angriffsstärke", "attack");
		imgMap.put("verteidigungsstärke", "defense");
		imgMap.put("willenskraft", "willpower");
		imgMap.put("geist", "spirit");
		imgMap.put("führung", "leadership");
		imgMap.put("wissen", "lore");
		imgMap.put("taktik", "tactics");
		imgMap.put("widerstand", "threat");
	}

	private StringBuilder imgAlts = new StringBuilder();
	private StringBuilder spans = new StringBuilder();

	public static void main(String[] args) throws IOException {
		CardHandlerChain chain = new CardHandlerChain();
		// chain.addCardHandler(new WriteLogCardHandler());
		WriteJsonCardHandler jsonHandler = new WriteJsonCardHandler("de");
		chain.addCardHandler(jsonHandler);
		CountCardHandler cch = new CountCardHandler();
		chain.addCardHandler(cch);

		DeckbauerParser parser = new DeckbauerParser();
		// List<CardSetInfo> csInfos = parser.parseCardSets();
		List<CardSetInfo> csInfos = parser.parseCardSetsWithCards();
		parser.parseCards(csInfos, chain);
		System.out.println(cch.getCount());

		PrintWriter writer = new PrintWriter("card_unique.json");
		writer.write(jsonHandler.toString());
		writer.flush();
		writer.close();
		log.info("total count: {}", jsonHandler.getCount());

		// new DeckbauerParser().parseCardSets();
	}

	public List<CardSetInfo> parseCardSetsWithCards() throws IOException {
		List<CardSetInfo> csInfos = new ArrayList<CardSetInfo>();
		csInfos.add(new CardSetInfo("Core Set;Grundspiel", URL_BASE.replace("${productId}", "1")));

		Document doc = Jsoup.connect(MAIN_URL).timeout(20000).get();
		Iterator<Element> iter = doc.select("#productid option").iterator();
		iter.next();
		iter.next();
		while (iter.hasNext()) {
			Element option = iter.next();
			String productId = option.attr("value");
			String names = option.text();
			int beginIdx = names.indexOf('(');
			int endIdx = names.indexOf(')');
			String name = names.substring(0, beginIdx).trim();
			String enName = names.substring(beginIdx + 1, endIdx).trim();
			CardSetInfo csInfo = new CardSetInfo(enName + ";" + name, URL_BASE.replace(
					"${productId}", productId));
			csInfos.add(csInfo);
			System.out.println(csInfo);
		}

		return csInfos;
	}

	public void parseCards(List<CardSetInfo> csInfos, CardHandlerChain chain) throws IOException {
		// int count = 0;
		for (CardSetInfo csInfo : csInfos) {
			// if (count++ > 3) {
			// break;
			// }
			parseCards(csInfo, chain);
		}
	}

	public void parseCards(CardSetInfo csInfo, CardHandlerChain handler) throws IOException {
		log.info("##### SET: {}", csInfo.getName());
		Document doc = Jsoup.connect(csInfo.getUrl()).timeout(20000).get();
		String text = doc.select(".searchinfo").get(1).text();
		int count = Integer.parseInt(text.split(" ")[0]);
		List<Card> cards = parseVerticalCards(doc);
		cards.addAll(parseHorizontalCards(doc));
		log.info("cards expected: {}, found: {}", count, cards.size());

		count = 0;
		for (Card card : cards) {
			card.setSetName(csInfo.getName().split(";")[0]);
			handler.handle(card);
			count++;
		}
	}

	public void parseScenariosAndEncounterSets() throws IOException {
		Map<String, String> scenMap = new HashMap<String, String>();
		Map<String, String> enstMap = new HashMap<String, String>();

		Document doc = Jsoup.connect(URL_SCEN).timeout(20000).get();
		Iterator<Element> iter = doc.select("div.stats").iterator();
		while (iter.hasNext()) {
			Element stat = iter.next();

			String scenario = stat.select("h1 a").get(0).text();
			if (scenario.toLowerCase().contains("(einfach)")
					|| scenario.toLowerCase().contains("(alptraum)")) {
				continue;
			}

			String[] texts = extractEnTextDeText(scenario);
			log.info("scenarion en: {}, de: {}", texts);
			scenMap.put(texts[0], texts[1]);

			Iterator<Element> enstIter = stat.select("ul li span").iterator();
			while (enstIter.hasNext()) {
				String encounterSet = enstIter.next().text();
				texts = extractEnTextDeText(encounterSet);
				log.info("  encounter set en: {}, de: {}", texts);
				enstMap.put(texts[0], texts[1]);
			}
		}

		List<ScenarioBase> scenarios = new ArrayList<ScenarioBase>();
		for (Entry<String, String> entry : scenMap.entrySet()) {
			ScenarioBase base = new ScenarioBase(Utils.toTechName(entry.getKey()));
			ScenarioLang lang = new ScenarioLang();
			lang.setName(entry.getValue());
			base.getLangItems().put("de", lang);
			scenarios.add(base);
		}
		JsonUtils.write(scenarios, "scenario_de.json");

		List<EncounterSetBase> encounterSets = new ArrayList<EncounterSetBase>();
		for (Entry<String, String> entry : enstMap.entrySet()) {
			EncounterSetBase base = new EncounterSetBase(Utils.toTechName(entry.getKey()));
			EncounterSetLang lang = new EncounterSetLang();
			lang.setName(entry.getValue());
			base.getLangItems().put("de", lang);
			encounterSets.add(base);
		}
		JsonUtils.write(encounterSets, "encounter_set_de.json");
	}

	public void parseCardSets() throws IOException {
		List<CardSetBase> cardSets = new ArrayList<CardSetBase>();

		Document doc = Jsoup.connect(URL_CRST).timeout(20000).get();
		Iterator<Element> iter = doc.select("div.stats h1 a").iterator();
		while (iter.hasNext()) {
			String text = iter.next().text();
			text = text.replace("(Albtraumdeck)", "Albtraumdeck").replace("(Nightmare Deck)",
					"Nightmare Deck");

			String[] texts = extractEnTextDeText(text);
			CardSetBase base = new CardSetBase(Utils.toTechName(texts[0]));
			CardSetLang lang = new CardSetLang();
			lang.setName(texts[1]);
			base.getLangItems().put("de", lang);
			cardSets.add(base);

			log.info("scenarion en: {}, de: {}", texts);

		}

		JsonUtils.write(cardSets, "card_set_de.json");
	}

	private String[] extractEnTextDeText(String input) {
		int beginIdx = input.indexOf('(');
		int endIdx = input.indexOf(')');
		String deText = input.substring(0, beginIdx).trim();
		String enText = input.substring(beginIdx + 1, endIdx).trim();
		return new String[] { enText, deText };
	}

	private List<Card> parseVerticalCards(Document doc) throws IOException {
		ArrayList<Card> output = new ArrayList<Card>();

		Elements divCardDetails = doc.select("div.card.vertical div.details");
		Iterator<Element> iter = divCardDetails.iterator();
		while (iter.hasNext()) {
			Card card = new Card();

			Element divCardDetail = iter.next();
			String names = divCardDetail.select(".header h3 a").get(0).text();
			int beginIdx = names.indexOf('(');
			int endIdx = names.indexOf(')');
			String name = names.substring(0, beginIdx).trim();
			String enName = names.substring(beginIdx + 1, endIdx).trim();
			String techName = Utils.toTechName(enName);
			log.debug("##### CARD name: {}, enName: {}, techName: {} #####", new Object[] { name,
					enName, techName });

			card.setTechName(techName);
			card.setName(name);

			String trait = StringUtils.trimToNull(divCardDetail.select("div.traits").get(0).text());
			trait = StringEscapeUtils.unescapeHtml(trait);
			if (trait != null) {
				log.debug("traits: {}", trait);
				card.setTrait(trait);
			}

			String text = "";
			Iterator<Element> divTextIter = divCardDetail.select(
					"div.text:eq(0) div.cardtext.lotrtext").iterator();
			while (divTextIter.hasNext()) {
				text += StringUtils.trimToEmpty(parseText(divTextIter.next()));
				if (divTextIter.hasNext()) {
					text += "\n\n";
				}
			}
			text = StringUtils.trimToNull(StringEscapeUtils.unescapeHtml(text));
			if (text != null) {
				log.debug("text: ", text);
				card.setText(text);
			}

			String errata = StringUtils.trimToNull(parseText(divCardDetail.select(
					"div.erratatext.lotrtext").get(0)));
			errata = StringEscapeUtils.unescapeHtml(errata);
			if (errata != null) {
				log.debug("errata: ", errata);
				card.setErrata(errata);
			}

			String footer = divCardDetail.select("div.footer").get(0).text();
			String pattern = "[0-9]x ([0-9]{1,3}) .*";
			Integer number = null;
			if (footer.matches(pattern)) {
				number = Integer.valueOf(footer.replaceFirst(pattern, "$1"));
				card.setNumber(number);
			} else {
				throw new IllegalStateException("Card number missing");
			}

			output.add(card);
		}

		return output;
	}

	private List<Card> parseHorizontalCards(Document doc) throws IOException {
		List<Card> output = new ArrayList<Card>();

		Elements divCardDetails = doc.select("div.card.horizontaldouble div.details");
		Iterator<Element> iter = divCardDetails.iterator();
		while (iter.hasNext()) {
			Card card = new Card();

			Element divCardDetail = iter.next();
			String names = divCardDetail.select(".header h3 a").get(0).text();
			int beginIdx = names.indexOf('(');
			int endIdx = names.indexOf(')');
			String name = names.substring(0, beginIdx).trim();
			String enName = names.substring(beginIdx + 1, endIdx).trim();
			String techName = Utils.toTechName(enName);
			log.debug("##### name: {}, enName: {}, techName: {} #####", new Object[] { name,
					enName, techName });

			card.setTechName(techName);
			card.setName(name);

			String text = "";
			Iterator<Element> divTextIter = divCardDetail.select(
					"div.text:eq(0) div.cardtext.lotrtext").iterator();
			while (divTextIter.hasNext()) {
				text += StringUtils.trimToEmpty(parseText(divTextIter.next()));
				if (divTextIter.hasNext()) {
					text += "\n\n";
				}
			}
			text = StringUtils.trimToNull(StringEscapeUtils.unescapeHtml(text));
			if (text != null) {
				log.debug("text: {}", text);
				card.setText(text);
			}

			String errata = StringUtils.trimToNull(parseText(divCardDetail.select(
					"div.erratatext.lotrtext").get(0)));
			errata = StringEscapeUtils.unescapeHtml(errata);
			if (errata != null) {
				log.debug("errata: ", errata);
				card.setErrata(errata);
			}

			String footer = divCardDetail.select("div.footer").get(0).text();
			String pattern = "[0-9]x ([0-9]{1,3}) .*";
			Integer number = null;
			if (footer.matches(pattern)) {
				number = Integer.valueOf(footer.replaceFirst(pattern, "$1"));
				card.setNumber(number);
			} else {
				throw new IllegalStateException("Card number missing");
			}

			output.add(card);
		}

		return output;
	}

	private String parseText(Element div) {
		String output = "";
		Iterator<Node> iter = div.childNodes().iterator();
		while (iter.hasNext()) {
			Node node = iter.next();
			if (node instanceof Element) {
				Element elem = (Element) node;
				if (elem.tagName().equals("br")) {
					output += "\n";
				} else if (elem.tagName().equals("img")) {
					String alt = elem.attr("alt").toLowerCase().trim();
					if (imgMap.containsKey(alt)) {
						output += "${" + imgMap.get(alt) + "}";
					} else {
						imgAlts.append(alt).append("\n");
					}
				} else if (elem.tagName().equals("span")) {
					String text = elem.toString();
					String classPattern = "<span class=\"(effect|errortext|trait)\">([^<]+)</span>";
					String stylePattern = "<span style=\"color: #36a6ed; font-weight: bold;\">([^<]+)</span>";
					if (text.matches(classPattern)) {
						output += text.replaceFirst(classPattern, "\\${$1}$2\\${$1}").replace(
								"${errortext}", "${error}");
					} else if (text.matches(stylePattern)) {
						output += text.replaceFirst(stylePattern, "\\${scenario}$1\\${scenario}");
					} else {
						spans.append(elem.toString()).append("\n");
					}
				}
			} else {
				output += node.toString();
			}
		}

		return output;
	}
}
